package com.wugh.fast.generator;

import com.google.common.base.CaseFormat;
import com.google.common.base.Strings;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Context;
import tk.mybatis.mapper.generator.MapperPlugin;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author WUGH
 * @date 2018/7/25
 */
public class FastEntityPlugin extends MapperPlugin {
    private Boolean isSjkjSqlite = false;
    private boolean useMapperCommentGenerator = true;

    private String deleteColumnName;
    private AnnotationParser isDeletedAnnotation;
    private AnnotationParser keySqlAnnotation;
    /**
     * instance方法
     */
    private Boolean generateDefaultInstanceMethod;
    /**
     * 字段常量
     */
    private Boolean generateColumnConsts;


    @Override
    public void setProperties(Properties properties) {
        this.isSjkjSqlite = Boolean.valueOf(StringUtils.defaultIfEmpty((String) properties.get("isSjkjSqlite"), "false"));
        //调用原函数之前去除generateDefaultInstanceMethod配置,即不使用原来的generateDefaultInstanceMethod生成方法
        this.generateDefaultInstanceMethod = Boolean.valueOf(StringUtils.defaultIfEmpty((String) properties.remove("generateDefaultInstanceMethod"), "false"));
        //调用原函数之前去除generateColumnConsts配置,即不使用原来的generateColumnConsts生成方法
        this.generateColumnConsts = Boolean.valueOf(StringUtils.defaultIfEmpty((String) properties.remove("generateColumnConsts"), "false"));
        super.setProperties(properties);
        String deleteColumnName = properties.getProperty("deleteColumnName");
        if (StringUtils.isNotBlank(deleteColumnName)) {
            this.deleteColumnName = deleteColumnName;
        }
        String isDeletedAnnotation = properties.getProperty("isDeletedAnnotation");
        if (StringUtils.isNotBlank(isDeletedAnnotation)) {
            this.isDeletedAnnotation = new AnnotationParser(isDeletedAnnotation);
        }
        String keySqlAnnotation = properties.getProperty("keySqlAnnotation");
        if (StringUtils.isNotBlank(keySqlAnnotation)) {
            this.keySqlAnnotation = new AnnotationParser(keySqlAnnotation);
        }
        if (useMapperCommentGenerator) {
            CommentGeneratorConfiguration configuration = context.getCommentGeneratorConfiguration();
            //把删除和跳过字段的配置信息放置到内容配置器中
            configuration.addProperty("deleteColumnName", properties.getProperty("deleteColumnName", ""));
            configuration.addProperty("isDeletedAnnotation", properties.getProperty("isDeletedAnnotation", ""));
            configuration.addProperty("keySqlAnnotation", properties.getProperty("keySqlAnnotation", ""));
            configuration.addProperty("skipInsertColumnNames", properties.getProperty("skipInsertColumnNames", ""));
            configuration.addProperty("skipUpdateColumnNames", properties.getProperty("skipUpdateColumnNames", ""));
            configuration.addProperty("isSjkjSqlite", properties.getProperty("isSjkjSqlite", ""));
        }

    }

    @Override
    public void setContext(Context context) {
        super.setContext(context);
        useMapperCommentGenerator = !"FALSE".equalsIgnoreCase(context.getProperty("useMapperCommentGenerator"));
        if (useMapperCommentGenerator) {
            CommentGeneratorConfiguration configuration = context.getCommentGeneratorConfiguration();
            configuration.setConfigurationType(FastCommentGenerator.class.getCanonicalName());
        }
        //支持oracle获取注释#114
        context.getJdbcConnectionConfiguration().addProperty("remarksReporting", "true");
        //支持mysql获取注释
        context.getJdbcConnectionConfiguration().addProperty("useInformationSchema", "true");
    }

    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        boolean result = super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
        //region 增加注释
        topLevelClass.addJavaDocLine("/**");
        topLevelClass.addJavaDocLine(" * 表名: " + introspectedTable.getFullyQualifiedTableNameAtRuntime());
        topLevelClass.addJavaDocLine(" * " + introspectedTable.getRemarks());
        topLevelClass.addJavaDocLine(" * @date " + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        topLevelClass.addJavaDocLine(" * @author 工具生成代码");
        topLevelClass.addJavaDocLine(" */");
        //endregion

        //region 处理@IsDeleted导包
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        if (StringUtils.isNotBlank(deleteColumnName) && isDeletedAnnotation != null) {
            //添加注解和导包
            Set<String> columnName = allColumns.stream().map(IntrospectedColumn::getActualColumnName).collect(Collectors.toSet());
            if (columnName.contains(this.deleteColumnName)) {
                isDeletedAnnotation.getFullyQualifiedJavaType().getImportList().forEach(topLevelClass::addImportedType);
            }
        }
        //endregion

        //region 处理@keySql导包
        if (keySqlAnnotation != null && (!introspectedTable.getPrimaryKeyColumns().isEmpty() || allColumns.stream().anyMatch(IntrospectedColumn::isIdentity))) {
            //有keysql注解,并且有主键
            keySqlAnnotation.getFullyQualifiedJavaType().getImportList().forEach(topLevelClass::addImportedType);

        }
        //endregion
        //region 处理常量
        if (generateColumnConsts) {
            List<Field> fields = topLevelClass.getFields();
            for (int i = 0; i < allColumns.size(); i++) {
                IntrospectedColumn introspectedColumn = allColumns.get(i);
                String constantKey = isSjkjSqlite
                        ? CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, SQLiteUtil.handleName(introspectedColumn.getActualColumnName()))
                        : CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, introspectedColumn.getActualColumnName());
                String javaProperty = isSjkjSqlite ? SQLiteUtil.handleName(introspectedColumn.getActualColumnName()) : introspectedColumn.getJavaProperty();
                Field field = new Field();
                field.setVisibility(JavaVisibility.PUBLIC);
                field.setStatic(true);
                field.setFinal(true);
                field.setName(constantKey); //$NON-NLS-1$
                field.setType(new FullyQualifiedJavaType(String.class.getName()));
                field.setInitializationString("\"" + javaProperty + "\"");
                context.getCommentGenerator().addClassComment(topLevelClass, introspectedTable);
                if (i == 0) {
                    //第一行增加 //region
                    field.addJavaDocLine("//region 数据库字段常量");
                }
                fields.add(i * 2, field);
                //增加字段名常量,用于pageHelper
                Field columnField = new Field();
                columnField.setVisibility(JavaVisibility.PUBLIC);
                columnField.setStatic(true);
                columnField.setFinal(true);
                columnField.setName("DB_" + constantKey);
                columnField.setType(new FullyQualifiedJavaType(String.class.getName()));
                columnField.setInitializationString("\"" + introspectedColumn.getActualColumnName() + "\"");
                if (i == allColumns.size() - 1) {
                    //最后一行增加 //endregion
                    columnField.setInitializationString(columnField.getInitializationString() + ";" + System.lineSeparator() + "\t//endregion");
                }
                fields.add(i * 2 + 1, columnField);
            }
        }
        //endregion

        //region 处理默认方法
        if (generateDefaultInstanceMethod) {
            HashMap<String, String> typeMap = new HashMap<>();
            typeMap.put("byte", "Byte");
            typeMap.put("short", "Short");
            typeMap.put("char", "Character");
            typeMap.put("int", "Integer");
            typeMap.put("long", "Long");
            typeMap.put("float", "Float");
            typeMap.put("double", "Double");
            typeMap.put("boolean", "Boolean");
            typeMap.put("String", "String");
            typeMap.put("BigDecimal", "BigDecimal");
            typeMap.put("BigInteger", "BigInteger");
            Set<String> supportTypes = Stream.concat(typeMap.keySet().stream(), typeMap.values().stream()).distinct().collect(Collectors.toSet());
            Method defaultMethod = new Method();
            //增加方法注释
            defaultMethod.addJavaDocLine("/**");
            defaultMethod.addJavaDocLine(" * 带默认值的实例");
            defaultMethod.addJavaDocLine("*/");
            defaultMethod.setStatic(true);
            defaultMethod.setName("defaultInstance");
            defaultMethod.setVisibility(JavaVisibility.PUBLIC);
            defaultMethod.setReturnType(topLevelClass.getType());
            defaultMethod.addBodyLine(String.format("%s instance = new %s();", topLevelClass.getType().getShortName(), topLevelClass.getType().getShortName()));
            for (IntrospectedColumn introspectedColumn : allColumns) {
                String shortName = introspectedColumn.getFullyQualifiedJavaType().getShortName();
                if (!supportTypes.contains(shortName)) {
                    continue;
                }
                if (introspectedColumn.getDefaultValue() != null) {
                    String defaultValue = introspectedColumn.getDefaultValue();
                    //处理备注中带有类型描述情况，如 postgresql中存在 ''::character varying
                    if (defaultValue.matches("'.*'::\\w+(\\s\\w+)?")) {
                        //
                        defaultValue = defaultValue.substring(0, defaultValue.lastIndexOf("::"));
                    }
                    //去除前后'',如 '123456' -> 123456
                    if (defaultValue.startsWith("'") && defaultValue.endsWith("'")) {
                        if (defaultValue.length() == 2) {
                            defaultValue = "";
                        } else {
                            defaultValue = defaultValue.substring(1, defaultValue.length() - 1);
                        }
                    }
                    String javaProperty = isSjkjSqlite ? SQLiteUtil.handleName(introspectedColumn.getActualColumnName()) : introspectedColumn.getJavaProperty();
                    String type = typeMap.getOrDefault(shortName, shortName);
                    switch (type) {
                        case "String":
                            defaultValue = "\"" + defaultValue + "\"";
                            type = null;
                            break;
                        case "Boolean":
                            //暂不支持时间类型默认值识别,不同数据库表达式不同
                            if ("0".equals(defaultValue)) {
                                defaultValue = "false";
                            } else if ("1".equals(defaultValue)) {
                                defaultValue = "true";
                            }
                            type = null;
                            break;
                        case "Float":
                            defaultValue = defaultValue + "F";
                            type = null;
                            break;
                        case "Long":
                            defaultValue = defaultValue + "L";
                            type = null;
                            break;
                        case "Integer":
                        case "Byte":
                        case "Short":
                        case "Double":
                            type = null;
                            break;
                        case "Character":
                            defaultValue = "'" + defaultValue + "'";
                            type = null;
                            break;
                        default:
                            defaultValue = "\"" + defaultValue + "\"";
                    }
                    if (type == null) {
                        defaultMethod.addBodyLine(String.format("instance.%s = %s;", javaProperty, defaultValue));
                    } else {
                        defaultMethod.addBodyLine(String.format("instance.%s = new %s(%s);", javaProperty, shortName, defaultValue));
                    }
                }
            }
            defaultMethod.addBodyLine("return instance;");
            topLevelClass.addMethod(defaultMethod);
        }
        //endregion
        return result;
    }

}
